package br.edu.ufcg.splab.techniques.reduction.heuristics;


import java.util.ArrayList;
import java.util.List;

import br.edu.ufcg.splab.generator.TestCase;
import br.edu.ufcg.splab.generator.TestSuite;
import br.edu.ufcg.splab.techniques.reduction.requirement.InterfaceRequirement;
import br.edu.ufcg.splab.techniques.reduction.requirement.RequirementTracer;


public class GreedyRedundancyEssential extends GreedyEssential {
		
	/**
	 * This heuristic was defined by Chen and Lau and is based on the following
	 * concepts:
	 * <p>1-to-1 redundancy strategy - A test case is said 1-to-1 redundant, 
	 * if the same set of requirements, that are covered by a test case, are covered by
	 * other test cases, then these test cases are considered 1-to-1 redundant;</p>
	 * 
	 * <p>Essential concept - selects all essential test cases. 
	 * A test case is essential when only this test case covers one specific requirement;</p>
	 * 
	 * <p>Greedy heuristic (presented in subsection \ref{G_Heuristic}).</p>
	 * 
	 * The essential and 1-to-1 strategies are applied alternatively,
	 * until there is no essential and 1-to-1 redundant test cases. The
	 * greedy strategy is only applied if neither the essential nor
	 * 1-to-1 redundancy can be applied.
	 * 
	 * @param suite The complete test suite.
	 * @param tracers The ordered list of requirements of the complete test suite.
	 * @return The reduced test suite.
	 */
	public TestSuite reduceTestSuite(TestSuite suite, List<RequirementTracer> tracers){
		List<TestCase> verifiedTests = new ArrayList<TestCase>(); 
		List<TestCase> reducedTests = new ArrayList<TestCase>();
		List<TestCase> toVerify = new ArrayList<TestCase>(suite.getTestSuite());
		
		for(RequirementTracer tracer: tracers){			
			reducedTests.addAll(verifiedTests);
			
			toVerify = new ArrayList<TestCase>(suite.getTestSuite());
			toVerify.removeAll(reducedTests);
			
			verifiedTests = new ArrayList<TestCase>();		
			
			List<InterfaceRequirement> requirements = tracer.getRequirements();
			for(TestCase tc: reducedTests){
				requirements.removeAll(tracer.getRequirementsSatisfied(tc));
			}
			List<InterfaceRequirement> notCovered = new ArrayList<InterfaceRequirement>(requirements);
			List<TestCase> allRedundants = new ArrayList<TestCase>();
			List<TestCase> essentials;
			List<TestCase> redundants;
			do {
				essentials = tracer.getEssentialTestCases(notCovered);
				for (TestCase testCase : essentials) {
					requirements = tracer.getRequirementsSatisfied(testCase);
					notCovered.removeAll(requirements);
					toVerify.remove(testCase);
					verifiedTests.add(testCase);
				}
				redundants = this.dismissRedundantTestCases(toVerify, tracer);		
				allRedundants.addAll(redundants);
			}while(essentials.size() > 0 && redundants.size() > 0);
						
			while(notCovered.size() > 0) {
				TestCase mostCoverer = this.getTheMostCovererOfNotSatisfied(toVerify, notCovered, tracer);
				requirements = tracer.getRequirementsSatisfied(mostCoverer);
				notCovered.removeAll(requirements);
				toVerify.remove(mostCoverer);
				verifiedTests.add(mostCoverer);
			}
			toVerify.addAll(allRedundants);			
		}
				
		reducedTests.addAll(verifiedTests);	
		
		TestSuite newTS = new TestSuite();
		newTS.addAll(reducedTests);
		
		return newTS;
	}
	
	private List<TestCase> dismissRedundantTestCases(List<TestCase> toVerify, RequirementTracer tracer) {
		List<TestCase> result = new ArrayList<TestCase>();
		List<TestCase> copyOfToVerify = new ArrayList<TestCase>(toVerify);
		TestCase redundant = this.getNextRedundantTestCase(copyOfToVerify, tracer);
		while(redundant != null) {
			copyOfToVerify = copyOfToVerify.subList(copyOfToVerify.indexOf(redundant), copyOfToVerify.size());
			copyOfToVerify.remove(0);
			toVerify.remove(redundant);
			result.add(redundant);
			redundant = this.getNextRedundantTestCase(copyOfToVerify, tracer);
		}
		return result;
	}

	private TestCase getNextRedundantTestCase(List<TestCase> toVerify, RequirementTracer tracer) {
		for (TestCase testCase : toVerify) {
			if(this.is1to1Redundant(testCase, toVerify, tracer)) {
				return testCase;
			}
		}		
		return null;
	}
	
	private boolean is1to1Redundant(TestCase redundantTestCase, List<TestCase> toVerify, RequirementTracer tracer) {
		for (TestCase testToBeVerified : toVerify) {
			if(testToBeVerified.equals(redundantTestCase)){
				continue;
			}
			List<InterfaceRequirement> reqsRedundantTestCase = tracer.getRequirementsSatisfied(redundantTestCase);
			List<InterfaceRequirement> reqsTestToBeVerified = tracer.getRequirementsSatisfied(testToBeVerified);
			if(reqsTestToBeVerified.containsAll(reqsRedundantTestCase)) {
				return true;
			}
		}
		return false;
	}
	
	/**
     * Provide a string representation of the technique to be written.
     */
	public String toString() {
		return "GRE";
	}
}
